#import "ZMQPlugin.h"
#import <Cordova/CDVPluginResult.h>
#include <stdio.h>
#import "AppDelegate.h"
//设置连接超时
#define TIME_OUT       20
//设置读取超时 -1 表示不会使用超时
#define READ_TIME_OUT  -1
//设置写入超时 -1 表示不会使用超时
#define WRITE_TIME_OUT -1
//每次最多读取多少
#define MAX_BUFFER     10240
//心跳间隔
#define heartTime      30

@interface ZMQPlugin ()
{
    NSString *_host; //IP地址
    NSInteger _port; //端口
    NSString *_token;//用户token
    NSString *_uid;
    NSString *tempMsg; //临时消息
    NSDate *lastTime; //最后一次接收到心跳的时间
    NSMutableString *temp; //接收消息temp

    int  tempID; //临时id
    int  number; //发送id次数
    BOOL isReceiveHeart; //是否收到心跳
}
@property (nonatomic, strong) GCDAsyncSocket *socket;//socket
@property (nonatomic, strong) NSTimer        *heartTimer;// 心跳计时器

@end

@implementation ZMQPlugin

static BOOL isSocketOfflineByUser = NO; //是否由用户断开

#pragma mark - zmqConnect -
-(void)zmqConnect:(CDVInvokedUrlCommand *)command {
    
    NSString *callbackId = [command callbackId];
    NSDictionary *params = [[command arguments]objectAtIndex:0];
    NSDictionary *url    = params[@"url"];
    _uid   = params[@"id"];
    _token = params[@"token"];
    _host  = url[@"ip"];
   _port  = [url[@"port"] integerValue];
    
    [self cutOffSocket];
    [self startConnectSocket:_host port:_port];
    [self successCallback:[self dictionaryToJson: @{@"message":@"链接success"}] callbackID:callbackId];
    temp = [[NSMutableString alloc]initWithString:@""];
}


#pragma mark - /** 开始连接socket */
- (void)startConnectSocket:(NSString*)addr port:(NSInteger)port {
    NSError *err;
    if (!self.socket) {
        self.socket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    [_socket connectToHost:_host onPort:_port error:&err];
    if (err) {
        NSLog(@"连接失败error=%@",err);
    }
}

#pragma mark - /** 传入ip地址和端口 */ -
- (NSInteger)SocketOpen:(NSString*)addr port:(NSInteger)port {
    if (![self.socket isConnected])
    {
        NSError *error = nil;
        [self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:&error];
    }
    return 0;
}

#pragma mark /** 连接到新的socket */
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket {
    NSLog(@"接收到新的socket啦");
}

#pragma mark /** 连接成功回调 */
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {

    NSLog(@"连接成功"); //这是异步返回的连接成功，
    [self.socket performBlock:^{
        [self.socket enableBackgroundingOnSocket];
    }];
    isSocketOfflineByUser = NO;
    tempID   = 0;
    number   = 0;
    lastTime = [NSDate date];
    /** 设置心跳 */
    if (!self.heartTimer) {
         self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:heartTime target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES];
    }
    /** 启动定时器 */
    [self.heartTimer fire];
    [self successCallback:[self dictionaryToJson:@{@"message":@"success"}] callbackID:@"连接成功"];
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(openPushNotification:) name:@"PushNotificationName" object:nil];
}

#pragma mark - /** 发送消息 */ -
- (void)sendMessage:(id)message {
    //向服务器发送数据
    NSString *str   = [message stringByAppendingString:@"\r\n"];//拼接结束符
    NSData *cmdData = [str dataUsingEncoding:NSUTF8StringEncoding];//字符串转data
    [self.socket writeData:cmdData withTimeout:WRITE_TIME_OUT tag:1];//写入
}

#pragma mark - //设置id -
- (void)setId:(NSString *)uid token:(NSString *)token {

    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    dic[@"command"]          = @"setId";
    dic[@"id"]               = uid;
    dic[@"token"]            = token;
    NSString *json           = [self dictionaryToJson:dic];//字典转json
    NSLog(@"设置id %@",json);
    [self sendMessage:json];
}

#pragma mark - zmqSendToPeer 发送-
- (void)zmqSendToPeer:(CDVInvokedUrlCommand *)command {

    NSString *callbackId = [command callbackId];
    NSString *msg        = [[command arguments] objectAtIndex:0];
    [self sendMessage:msg];
    tempMsg              = callbackId;
}

#pragma mark - /** 发送消息成功之后回调 */ -
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag {
    NSLog(@"发送消息成功");
    //读取消息???why
    [self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0];
    [self successCallback:[self dictionaryToJson: @{@"message":@"success"}] callbackID:tempMsg];
}

#pragma mark - zmqDisConnect 断开连接-
- (void)zmqDisConnect:(CDVInvokedUrlCommand *)command {
    NSString *callbackId = [command callbackId];

    [self cutOffSocket];
    [self successCallback:[self dictionaryToJson: @{@"message":@"success"}] callbackID:callbackId];
}

#pragma mark - /** 断开socket */ -
-(void)cutOffSocket {
    
    isSocketOfflineByUser = YES;
    [self.socket disconnect];
    [self.heartTimer invalidate]; //停用定时器
    self.heartTimer = nil;//销毁定时器
}

#pragma; mark -  /** 心跳连接 */
-(void)checkLongConnectByServe {
    
    //如果第一次id没有发送成功, 要再发一次, 3次失败后就断开重连
    if (tempID == 0) {
        [self setId:_uid token:_token];
        number += 1;
        if (number >= 3) {
            [self cutOffSocket];
            [self startConnectSocket:_host port:_port];
            number = 0;
        }
    } else {  //收到202之后才发心跳      // 向服务器发送固定的消息，来检测长连接

        NSMutableDictionary *dic = [NSMutableDictionary dictionary];
        dic[@"command"]          = @"HeartBeat";
        NSString *longConnect    = [self dictionaryToJson:dic];
        [self sendMessage:longConnect];

        // 超时未接收到心跳重连
        NSTimeInterval myDate    = [lastTime timeIntervalSinceNow];
        NSLog(@"mydate:  %f", myDate);
        
        if (myDate < - heartTime * 3) { //超过3次时间还没有收到心跳就断开重连
            [self cutOffSocket];
            [self startConnectSocket:_host port:_port];
        }
    }
}

#pragma mark /** 接受消息成功之后回调 */
- (void)socket:(GCDAsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag {

//    NSLog(@"收到消息哈哈: %@", data);
    //服务端返回消息数据量比较大时，可能分多次返回。所以在读取消息的时候，设置MAX_BUFFER表示每次最多读取多少，当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息，然后才解析
    isReceiveHeart = YES;//收到心跳
    lastTime       = [NSDate date];
    if(data.length < MAX_BUFFER )
    {
        NSString *dataStr = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        if (tempID == 0) {
            if ([dataStr isEqualToString:@"202\r\n"]) { //如果收到202说明服务端收到了设置的id, 则正常进行后续
                tempID = 1;
            }
        }
        
        if (![dataStr isEqualToString:@"202\r\n"] && ![dataStr isEqualToString:@"200\r\n"]) {
            
            NSString *str = [NSString stringWithFormat:@"%@%@", temp, dataStr];
            temp = [[NSMutableString alloc]initWithString:@""];
            
            NSMutableArray *strArray = [NSMutableArray arrayWithArray:[str componentsSeparatedByString:@"\r\n"]]; // 分割字符串
            /**如果消息最后没有结束符, 要把最后一个\r\n后面的消息截掉, 存入temp, 拼接到下一次的消息前面*/
            if (![str hasSuffix:@"\r\n"]) {
                [temp appendString:[strArray lastObject]]; //拼接字符串
            }
            [strArray removeLastObject]; //移除最后一个object
            for (NSString *str1 in strArray) {

                [self successCallback:[self dictionaryToJson: @{@"message":str1}] callbackID:str1];
                NSDictionary *userInfo = @{@"message": str1};//存入字典
                NSDictionary *dic      = [self dictionaryWithJsonString:str1];//json转字典
                NSString *json         = [self dictionaryToJson:userInfo];// 字典转json
                
                if ([dic[@"type"] integerValue] != 2 && [dic[@"type"] integerValue] != 21) {
                    [self.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireDocumentEvent('zmq.messages',%@)", json]];
                } else {
                    if([dic[@"type"] integerValue] == 21) {
                        [self.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireDocumentEvent('zmq.notification',%@)",json]];
                    }
                }
            }
        }
    }
    //重点: 使用readDataToData替代readDataWithTimeout, 并且使用GCDAsyncSocket解决丢包问题
    NSData *newline = [@"\r\n" dataUsingEncoding:NSASCIIStringEncoding];
    [self.socket readDataToData:newline withTimeout:READ_TIME_OUT tag:0];
}

//点击推送消息
- (void)openPushNotification:(NSNotification *)notification {
    
    NSLog(@"接收推送消息%@", notification);
    NSString *content = [[[notification.userInfo objectForKey:@"aps"] objectForKey:@"alert"] objectForKey:@"content"];
    NSDictionary *dic = @{@"message": content};//存入字典
    NSString *json = [self dictionaryToJson:dic];// 字典转json
    [self.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireDocumentEvent('zmq.notification',%@)",json]];
}



#pragma mark - /** 断开连接代理 */ -
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
    NSLog(@"连接失败");
    if (err) {
        NSLog(@"错误信息: %@", err);
    }
    if (isSocketOfflineByUser || err.code == 57) {
        [self errorCallback:[self dictionaryToJson: @{@"message":@"failed"}] callbackID:@"连接断开"];
        return;
    } else {  //服务器掉线，重连

        [self.socket disconnect];
        [self.heartTimer invalidate]; //停用定时器
        self.heartTimer = nil;//销毁定时器
        
        [self startConnectSocket:_host port:_port];
        lastTime = [NSDate date];//时间重置
    }
    [[NSNotificationCenter defaultCenter]removeObserver:self name:@"PushNotificationName" object:nil];
}

#pragma mark - 成功回调 -
- (void)successCallback:(NSString *)sucStr callbackID: (NSString *)callbackID{
    
    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:sucStr];
    [self.commandDelegate sendPluginResult:result callbackId:callbackID];
}

#pragma mark - 失败回调 -
- (void)errorCallback:(NSString *)err callbackID: (NSString *)callbackID{
    
    CDVPluginResult *errorResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:err];
    [self.commandDelegate sendPluginResult:errorResult callbackId:callbackID];
}

#pragma mark - 字典->json字符串 -
- (NSString *)dictionaryToJson:(NSDictionary *)dic {
    
    NSError *parseError = nil;
    //字典转data
    NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
    return [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; //data转json
}

#pragma mark - json字符串->字典 -
- (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString {
    
    if (jsonString == nil) {
        
        return nil;
    }
    //json转data
    NSData *jsonData  = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *err;
    //json解析
    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData
                         
                                                        options:NSJSONReadingMutableContainers
                         
                                                          error:&err];
    
    if(err) {
        
        NSLog(@"json解析失败啦啦：%@",err);
        
        return nil;
    }
    return dic;
}

//获取devicetoken
- (NSString *)getDeviceToken:(CDVInvokedUrlCommand *)command {
    
    NSString *callbackId  = [command callbackId];
    NSString *deviceToken = [[NSUserDefaults standardUserDefaults]objectForKey:@"DeviceToken"];
    NSLog(@" device : %@", deviceToken);
    if(!deviceToken || [deviceToken isEqualToString:@""]) {
        [self errorCallback:@"获取失败" callbackID:@"error"];
    } else {
        [self successCallback:[self dictionaryToJson: @{@"deviceToken":deviceToken}] callbackID:callbackId];
    }
    return deviceToken;
}


@end
